﻿#include "stdafx.h"
#include <Strsafe.h>
BOOL _dx_init(INT* nError)
{
    BOOL          ret        = FALSE;
    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };
    UINT numFeatureLevels = ARRAYSIZE(featureLevels);
    D3D_FEATURE_LEVEL featureLevel;
    *nError =
        D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
            featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &g_Ri.pD3DDevice, &featureLevel, &g_Ri.pD3DDeviceContext);
    if (*nError == 0 && g_Ri.pD3DDevice != nullptr) {
        g_Ri.pD3DDevice->QueryInterface(__uuidof(ID3D11Device1), (void**)&g_Ri.pD3DDevice1);
#ifdef _DEBUG   // 如果为Debug模式，则启用D2D调试层

        // 下面为设置启用调试层，会在IDE输出窗口内显示,消息框和菜单项目会有彩色外框
        D2D1_FACTORY_OPTIONS options;
        options.debugLevel = D2D1_DEBUG_LEVEL_ERROR;
        // 创建工厂
        //*nError = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, options, &
        // g_Ri.pD2Dfactory);
        *nError = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &g_Ri.pD2Dfactory);
#else
        *nError = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &g_Ri.pD2Dfactory);
#endif
        if (*nError == 0) {
            *nError = g_Ri.pD3DDevice->QueryInterface(__uuidof(IDXGIDevice), (LPVOID*)&g_Ri.pDXGIDevice);
            if (*nError == 0 && g_Ri.pDXGIDevice != nullptr) {
                *nError = g_Ri.pD2Dfactory->CreateDevice(g_Ri.pDXGIDevice, &g_Ri.pD2DDevice);
                if (*nError == 0 && g_Ri.pD2DDevice != nullptr) {
                    *nError = g_Ri.pD2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
                                                                   &g_Ri.pD2DDeviceContext);
                    if (*nError == 0 && g_Ri.pD2DDeviceContext != nullptr) {
                        g_Ri.pD2DDeviceContext->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
                        g_Ri.pGDIInterop = _dx_get_gdiInterop(g_Ri.pD2DDeviceContext);
                        *nError          = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
                                                               __uuidof(IDWriteFactory),
                                                               (IUnknown**)&g_Ri.pDWriteFactory);
                        if (*nError == 0) {
                            WCHAR LocaleName[260];
                            INT   reta = GetUserDefaultLocaleName(LocaleName, 260) * 2;
                            if (reta > 0) {
                                g_Ri.pLocaleName = Ex_MemAlloc(reta);
                                RtlMoveMemory(g_Ri.pLocaleName, LocaleName, reta);
                            }
                            ret = TRUE;
                        }
                    }
                }
            }
        }
    }
    return ret;
}

void _dx_uninit()
{
    if (g_Ri.pLocaleName) {
        Ex_MemFree(g_Ri.pLocaleName);
    }
    if (g_Ri.pEffectGaussianBlur) {
        g_Ri.pEffectGaussianBlur->Release();
    }
    if (g_Ri.pEffectHueRotation) {
        g_Ri.pEffectHueRotation->Release();
    }
    if (g_Ri.pGDIInterop) {
        g_Ri.pGDIInterop->Release();
    }
    if (g_Ri.pD2DDeviceContext) {
        g_Ri.pD2DDeviceContext->Release();
    }
    if (g_Ri.pD2DDevice) {
        g_Ri.pD2DDevice->Release();
    }
    if (g_Ri.pD2Dfactory) {
        g_Ri.pD2Dfactory->Release();
    }
    if (g_Ri.pDWriteFactory) {
        g_Ri.pDWriteFactory->Release();
    }
}

ID2D1GdiInteropRenderTarget* _dx_get_gdiInterop(ID2D1DeviceContext* pDeviceContext)
{
    ID2D1GdiInteropRenderTarget* pGDIInterface = nullptr;
    ((ID2D1DeviceContext*)pDeviceContext)
        ->QueryInterface(__uuidof(ID2D1GdiInteropRenderTarget), (LPVOID*)&pGDIInterface);
    return pGDIInterface;
}

ID2D1Bitmap* _dx_createbitmap(ID2D1DeviceContext* pDeviceContext, INT width, INT height,
                              INT* nError)
{

    D2D1_SIZE_U size;
    size.width  = width;
    size.height = height;

    D2D1_BITMAP_PROPERTIES1 pro = {};
    pro.pixelFormat.alphaMode   = D2D1_ALPHA_MODE_PREMULTIPLIED;
    pro.pixelFormat.format      = DXGI_FORMAT_B8G8R8A8_UNORM;
    pro.dpiX                    = 96;
    pro.dpiY                    = 96;

    pro.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE;

    ID2D1Bitmap1* pBitmap = nullptr;
    if (nError) {
        *nError = pDeviceContext->CreateBitmap(size, NULL, 0, pro, (ID2D1Bitmap1**)&pBitmap);
    }
    return pBitmap;
}

void _dx_settarget(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap)
{
    // if (pBitmap)
    // {
    pDeviceContext->SetTarget(pBitmap);
    // }
}

void _dx_getsize(ID2D1Bitmap* pBitmap, FLOAT* width, FLOAT* height)
{
    D2D1_SIZE_F size = pBitmap->GetSize();
    *width           = size.width;
    *height          = size.height;
}

ID2D1Bitmap* _dx_gettarget(ID2D1DeviceContext* pDeviceContext)
{
    ID2D1Bitmap* pImage = nullptr;
    pDeviceContext->GetTarget((ID2D1Image**)&pImage);
    return pImage;
}

void _dx_begindraw(ID2D1DeviceContext* pDeviceContext)
{
    pDeviceContext->BeginDraw();
}

INT _dx_enddraw(ID2D1DeviceContext* pDeviceContext)
{
    INT nError = pDeviceContext->EndDraw();
    if (nError != 0) {
        Ex_SetLastError(nError);
    }
    return nError;
}

void _dx_flush(ID2D1DeviceContext* pDeviceContext)
{
    pDeviceContext->Flush();
}

void _dx_clear(ID2D1DeviceContext* pDeviceContext, EXARGB Color)
{
    D2D1_COLOR_F color_f = {0};
    ARGB2ColorF(Color, &color_f);
    pDeviceContext->Clear(color_f);
}

BOOL _dx_createeffect(ID2D1DeviceContext* pDeviceContext, IID peffectId, ID2D1Effect** peffect,
                      INT* nError)
{
    ID2D1Effect* ret = nullptr;
    *nError          = pDeviceContext->CreateEffect(peffectId, &ret);
    *peffect         = ret;
    return nError == 0;
}

void _dx_blur(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap, FLOAT fDeviation,
              RECT* lprc, INT* nError)
{
    _dx_flush(pDeviceContext);
    if (g_Ri.pEffectGaussianBlur == 0) {
        _dx_createeffect(pDeviceContext, CLSID_D2D1GaussianBlur, &g_Ri.pEffectGaussianBlur, nError);
        INT    hard     = 1;
        UINT32 datasize = 4;
        *nError         = g_Ri.pEffectGaussianBlur->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE,
                                                             D2D1_BORDER_MODE_SOFT);
    }
    if (*nError == 0) {
        D2D1_SIZE_F   size     = {};
        D2D1_POINT_2F ptOffset = {};
        if (lprc == 0) {
            size = pBitmap->GetSize();
        }
        else {
            ptOffset.x  = (FLOAT)lprc->left;
            ptOffset.y  = (FLOAT)lprc->top;
            size.width  = (FLOAT)lprc->right - ptOffset.x;
            size.height = (FLOAT)lprc->bottom - ptOffset.y;
        }

        ID2D1Bitmap* pCopyBitmap =
            _dx_createbitmap(pDeviceContext, (INT)size.width, (INT)size.height, nError);
        if (*nError == 0) {
            pCopyBitmap->CopyFromBitmap(NULL, pBitmap, (D2D_RECT_U*)lprc);
            g_Ri.pEffectGaussianBlur->SetInput(0, pCopyBitmap, TRUE);

            FLOAT fScale = fDeviation / 2;
            *nError = g_Ri.pEffectGaussianBlur->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
                                                         (BYTE*)&fScale, 4);
            if (*nError == 0) {
                ID2D1Image* output = nullptr;
                g_Ri.pEffectGaussianBlur->GetOutput(&output);
                if (output != 0) {
                    pDeviceContext->DrawImage(output, ptOffset, D2D1_INTERPOLATION_MODE_LINEAR,
                                              D2D1_COMPOSITE_MODE_SOURCE_OVER);
                    output->Release();
                }
            }
            pCopyBitmap->Release();
        }
    }
}

void _dx_composite(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap1, ID2D1Bitmap* pBitmap2,
                   D2D1_COMPOSITE_MODE mode, INT* nError)
{
    _dx_flush(pDeviceContext);
    if (g_Ri.pEffectComposite == 0) {
        _dx_createeffect(pDeviceContext, CLSID_D2D1Composite, &g_Ri.pEffectComposite, nError);
    }
    if (*nError == 0) {
        g_Ri.pEffectComposite->SetInput(0, pBitmap1);
        g_Ri.pEffectComposite->SetInput(1, pBitmap2);
        pDeviceContext->BeginDraw();
        pDeviceContext->DrawImage(g_Ri.pEffectComposite, D2D1_INTERPOLATION_MODE_LINEAR, mode);
        pDeviceContext->EndDraw();
    }
}

void _dx_cliprect(ID2D1DeviceContext* pDeviceContext, FLOAT left, FLOAT top, FLOAT right,
                  FLOAT bottom)
{
    D2D1_RECT_F rect = {0};
    rect.left        = left;
    rect.top         = top;
    rect.right       = right;
    rect.bottom      = bottom;
    pDeviceContext->PushAxisAlignedClip(
        rect, (D2D1_ANTIALIAS_MODE)Flag_Query(ENGINE_FLAG_RENDER_CANVAS_ALIAS));
}

void _dx_resetclip(ID2D1DeviceContext* pDeviceContext)
{
    pDeviceContext->PopAxisAlignedClip();
}

void _dx_drawbitmaprect(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap, FLOAT dstLeft,
                        FLOAT dstTop, FLOAT dstRight, FLOAT dstBottom, INT dwAlpha)
{
    D2D1_RECT_F rect = {0};
    rect.left        = dstLeft;
    rect.top         = dstTop;
    rect.right       = dstRight;
    rect.bottom      = dstBottom;
    pDeviceContext->DrawBitmap(pBitmap, rect, (FLOAT)(dwAlpha / 255.0),
                               D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
}

void _dx_drawbitmaprectrect(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap, FLOAT dstLeft,
                            FLOAT dstTop, FLOAT dstRight, FLOAT dstBottom, FLOAT srcLeft,
                            FLOAT srcTop, FLOAT srcRight, FLOAT srcBottom, INT dwAlpha)
{
    D2D1_RECT_F rect  = {0};
    rect.left         = dstLeft;
    rect.top          = dstTop;
    rect.right        = dstRight;
    rect.bottom       = dstBottom;
    D2D1_RECT_F rect2 = {0};
    rect2.left        = srcLeft;
    rect2.top         = srcTop;
    rect2.right       = srcRight;
    rect2.bottom      = srcBottom;
    pDeviceContext->DrawBitmap(pBitmap, rect, (FLOAT)(dwAlpha / 255.0),
                               D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rect2);
}

void _dx_drawimage(ID2D1DeviceContext* pDeviceContext, img_s* pImage, FLOAT left, FLOAT top,
                   INT mode)
{
    D2D1_POINT_2F point = {};
    point.x             = left;
    point.y             = top;
    pDeviceContext->DrawImage((ID2D1Image*)pImage, point, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
                              (D2D1_COMPOSITE_MODE)mode);
}

void _dx_drawimage_ex(ID2D1DeviceContext* pDeviceContext, img_s* pImage, FLOAT dstLeft,
                      FLOAT dstTop, FLOAT srcLeft, FLOAT srcTop, FLOAT srcRight, FLOAT srcBottom,
                      INT mode)
{
    D2D1_POINT_2F point = {0};
    point.x             = dstLeft;
    point.y             = dstTop;
    D2D1_RECT_F rect    = {0};
    rect.left           = srcLeft;
    rect.top            = srcTop;
    rect.right          = srcRight;
    rect.bottom         = srcBottom;
    pDeviceContext->DrawImage((ID2D1Image*)pImage, point, rect, D2D1_INTERPOLATION_MODE_LINEAR,
                              (D2D1_COMPOSITE_MODE)mode);
}

void _dx_bmp_copyfrom(ID2D1Bitmap** pDestBitmap, ID2D1Bitmap* pSrcBitmap, INT dX, INT dY,
                      INT srcLeft, INT srcTop, INT srcRight, INT srcBottom)
{
    D2D1_POINT_2U point = {0};
    point.x             = dX;
    point.y             = dY;
    D2D1_RECT_U rect    = {0};
    rect.left           = srcLeft;
    rect.top            = srcTop;
    rect.right          = srcRight;
    rect.bottom         = srcBottom;
    (*pDestBitmap)->CopyFromBitmap(&point, pSrcBitmap, &rect);
}

void _dx_rotate_hue(ID2D1DeviceContext* pDeviceContext, ID2D1Bitmap* pBitmap, FLOAT fAngle,
                    INT* nError)
{
    _dx_flush(pDeviceContext);
    if (g_Ri.pEffectHueRotation == 0) {
        _dx_createeffect(pDeviceContext, CLSID_D2D1HueRotation, &g_Ri.pEffectHueRotation, nError);
    }
    if (*nError == 0) {
        D2D1_SIZE_F szf = pBitmap->GetSize();

        ID2D1Bitmap* pCopyBitmap = _dx_createbitmap(pDeviceContext, szf.width, szf.height, nError);
        if (*nError == 0) {
            pCopyBitmap->CopyFromBitmap(NULL, pBitmap, NULL);
            g_Ri.pEffectHueRotation->SetInput(0, pCopyBitmap, 1);
            *nError = g_Ri.pEffectHueRotation->SetValue(0, (BYTE*)&fAngle, 4);
            if (*nError == 0) {
                ID2D1Image* output = nullptr;
                g_Ri.pEffectHueRotation->GetOutput(&output);
                if (output != 0) {
                    pDeviceContext->DrawImage(output, NULL, NULL,
                                              D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
                                              D2D1_COMPOSITE_MODE_SOURCE_COPY);
                    output->Release();
                }
            }
            pCopyBitmap->Release();
        }
    }
}

void ARGB2ColorF(EXARGB argb, D2D1_COLOR_F* color)
{
    color->r = (FLOAT)((argb >> 16) & 0xFF) / 255;
    color->g = (FLOAT)((argb >> 8) & 0xFF) / 255;
    color->b = (FLOAT)(argb & 0xFF) / 255;
    color->a = (FLOAT)((argb >> 24) & 0xFF) / 255;
}

void ColorF2ARGB(D2D1_COLOR_F color, EXARGB* argb)
{
    if (argb) {
        *argb = (EXARGB)(ExARGB((INT)color.r, (INT)color.g, (INT)color.b, (INT)color.a));
    }
}

D2DPrintJobChecker::D2DPrintJobChecker() :
    m_eventCookie(0),
    m_refcount(1),
    m_completionEvent(nullptr),
    m_connectionPoint(nullptr),
    m_isInitialized(false)
{
    PrintDocumentPackageStatus status = { 0 };
    m_documentPackageStatus = status;

    // Initialize the critical section, one time only.
    InitializeCriticalSection(&m_criticalSection);
}

D2DPrintJobChecker::~D2DPrintJobChecker()
{
    ReleaseResources();

    // Release resources used by the critical section object.
    DeleteCriticalSection(&m_criticalSection);
}

// Initialize this print job checker by registering a print document package target.
// This is required before the application can use this print job checker to monitor a print job.
HRESULT D2DPrintJobChecker::Initialize(
    _In_ IPrintDocumentPackageTarget* documentPackageTarget
)
{
    // Application can only initialize this job checker once.
    if (m_isInitialized)
    {
        return E_FAIL;
    }

    HRESULT hr = (documentPackageTarget != nullptr) ? S_OK : E_INVALIDARG;

    IConnectionPointContainer* connectionPointContainer = nullptr;
    if (SUCCEEDED(hr))
    {
        hr = documentPackageTarget->QueryInterface(IID_PPV_ARGS(&connectionPointContainer));
    }

    if (SUCCEEDED(hr))
    {
        hr = connectionPointContainer->FindConnectionPoint(
            __uuidof(IPrintDocumentPackageStatusEvent),
            &m_connectionPoint
        );
    }

    IUnknown* unknownEvent = nullptr;
    unknownEvent = static_cast<IPrintDocumentPackageStatusEvent*>(this);

    if (SUCCEEDED(hr))
    {
        hr = m_connectionPoint->Advise(unknownEvent, &m_eventCookie);
    }

    // Create an event handle for print job completion.
    if (SUCCEEDED(hr))
    {
        m_completionEvent = ::CreateEvent(
            nullptr, // The handle cannot be inherited by child processes.
            TRUE,    // The event object requires the use of the ResetEvent function to set the event state to nonsignaled.
            FALSE,   // The initial state of the event object is nonsignaled.
            nullptr  // Name of the event object.
        );
        hr = (!m_completionEvent) ? HRESULT_FROM_WIN32(::GetLastError()) : hr;
    }

    if (connectionPointContainer)
    {
        ULONG num = connectionPointContainer->Release();
        connectionPointContainer = nullptr;
    }

    if (SUCCEEDED(hr))
    {
        m_isInitialized = true;
    }
    else
    {
        ReleaseResources();
    }

    return hr;
}

HRESULT STDMETHODCALLTYPE
D2DPrintJobChecker::QueryInterface(
    REFIID iid,
    _Out_ void** ppvObject
)
{
    if (iid == __uuidof(IUnknown) ||
        iid == __uuidof(IPrintDocumentPackageStatusEvent))
    {
        *ppvObject = static_cast<IPrintDocumentPackageStatusEvent*>(this);
        AddRef();
        return S_OK;
    }
    else
    {
        *ppvObject = nullptr;
        return E_NOINTERFACE;
    }
}

ULONG STDMETHODCALLTYPE
D2DPrintJobChecker::AddRef()
{
    return (ULONG)InterlockedIncrement(&m_refcount);
}

ULONG STDMETHODCALLTYPE
D2DPrintJobChecker::Release()
{
    ULONG res = (ULONG)InterlockedDecrement(&m_refcount);
    if (0 == res)
    {
        delete this;
    }
    return res;
}

HRESULT D2DPrintJobChecker::GetTypeInfoCount(
    _Out_ UINT* /* pctinfo */
)
{
    return E_NOTIMPL;
}

HRESULT D2DPrintJobChecker::GetTypeInfo(
    UINT /* iTInfo */,
    LCID /* lcid */,
    _Outptr_result_maybenull_ ITypeInfo** /* ppTInfo */
)
{
    return E_NOTIMPL;
}

HRESULT D2DPrintJobChecker::GetIDsOfNames(
    _In_                        REFIID     /* riid */,
    _In_reads_(cNames)          LPOLESTR*  /* rgszNames */,
    _In_range_(0, 16384)        UINT       cNames,
    LCID       /* lcid */,
    __out_ecount_full(cNames)   DISPID*    /* rgDispId */
)
{
    UNREFERENCED_PARAMETER(cNames);
    return E_NOTIMPL;
}

HRESULT D2DPrintJobChecker::Invoke(
    DISPID          /* dispIdMember */,
    REFIID          /* riid */,
    LCID            /* lcid */,
    WORD            /* wFlags */,
    DISPPARAMS*     /* pDispParams */,
    VARIANT*        /* pVarResult */,
    EXCEPINFO*      /* pExcepInfo */,
    UINT*           /* puArgErr */
)
{
    return E_NOTIMPL;
}

// Callback to indicate that the status of the package has changed.
STDMETHODIMP D2DPrintJobChecker::PackageStatusUpdated(
    _In_ PrintDocumentPackageStatus* packageStatus
)
{
    HRESULT hr = (packageStatus == nullptr) ? E_INVALIDARG : S_OK;

    if (SUCCEEDED(hr))
    {
        // The package status update operation is guarded with a critical section,
        // since PackageStatusUpdated may be called in a very short time slot.
        EnterCriticalSection(&m_criticalSection);
        m_documentPackageStatus = *packageStatus;
        LeaveCriticalSection(&m_criticalSection);
    }

    if (SUCCEEDED(hr))
    {
        // Signal the print job complete event.
        if (PrintDocumentPackageCompletion_InProgress != m_documentPackageStatus.Completion)
        {
            if (!SetEvent(m_completionEvent))
            {
                hr = HRESULT_FROM_WIN32(::GetLastError());
            }
        }
    }

    switch (m_documentPackageStatus.Completion)
    {
    case PrintDocumentPackageCompletion_Completed:
        ReleaseResources();
        OutputDebugString(L"PrintDocumentPackageCompletion_Completed; ");
        break;

    case PrintDocumentPackageCompletion_Canceled:
        OutputDebugString(L"PrintDocumentPackageCompletion_Canceled; ");
        break;

    case PrintDocumentPackageCompletion_Failed:
        OutputDebugString(L"PrintDocumentPackageCompletion_Failed; ");
        break;

    case PrintDocumentPackageCompletion_InProgress:
        OutputDebugString(L"PrintDocumentPackageCompletion_InProgress; ");
        break;

    default:
        OutputDebugString(L"PrintDocumentPackageStatus unknown! ");
        break;
    }
#if defined(_DEBUG)
    WCHAR messageBuffer[256] = { 0 };
    StringCchPrintf(
        messageBuffer,
        ARRAYSIZE(messageBuffer),
        L"\tjobID:%3d, currentDoc:%3d, currentPage:%3d, currentPageTotal:%3d, packageStatus:0x%08X\n",
        m_documentPackageStatus.JobId,
        m_documentPackageStatus.CurrentDocument,
        m_documentPackageStatus.CurrentPage,
        m_documentPackageStatus.CurrentPageTotal,
        m_documentPackageStatus.PackageStatus
    );
    OutputDebugString(messageBuffer);
#endif
    return S_OK;
}

// Return print document package status.
PrintDocumentPackageStatus D2DPrintJobChecker::GetStatus()
{
    return(m_documentPackageStatus);
}

// Wait for job completion event to be signaled; in the meanwhile keep pumping messages to avoid deadlocks.
//
// This is an example for deadlock: when user print to a " Print To File" printer, the winspool.dll will try to pop
// up a modal dialog asking for file name. In order to show modal dialog, winspool will call EnableWindow(FALSE) to
// disable this window. Therefore, the winspool thread is waiting for us to process the message of disabling the
// window, while our thread is waiting for the winspool thread to complete.
HRESULT D2DPrintJobChecker::WaitForCompletion()
{
    // Return successfully if this job checker is not initialized.
    if (!m_isInitialized)
    {
        return S_OK;
    }

    HRESULT hr = S_OK;

    bool isWaiting = true;
    while (isWaiting)
    {
        DWORD waitResult = ::MsgWaitForMultipleObjects(
            1,                  // Number of object handles in the array.
            &m_completionEvent, // Array of object handles.
            FALSE,              // Function returns when the state of any one of the objects is set to signaled.
            INFINITE,           // Function will return only when the specified objects are signaled.
            QS_ALLINPUT         // Any message is in the queue.
        );
        if (waitResult == WAIT_OBJECT_0 + 1)
        {
            // This is not the desired completion event.
            MSG msg;
            while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
            {
                ::DispatchMessage(&msg);
            }
            if (msg.message == WM_QUIT)
            {
                isWaiting = false;
                break;
            }
        }
        else
        {
            isWaiting = false;
        }
    }

    return hr;
}



// Release resources.
void D2DPrintJobChecker::ReleaseResources()
{
    if (m_connectionPoint != nullptr)
    {
        if (m_eventCookie != 0)
        {
            HRESULT hr = m_connectionPoint->Unadvise(m_eventCookie);
            if (SUCCEEDED(hr))
            {
                m_eventCookie = 0; // 成功后立即重置
            }
        }
        m_connectionPoint->Release();
        m_connectionPoint = nullptr;
    }

    if (m_completionEvent != nullptr)
    {
        ::CloseHandle(m_completionEvent);
        m_completionEvent = nullptr;
    }

    return;
}